package manage.modules.rbac.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import manage.commons.Constants;
import manage.commons.utils.NumberAndBoolean;
import manage.commons.utils.TableNameUtil;
import manage.modules.BaseService;
import manage.modules.action.model.ActionRange;
import manage.modules.action.model.ActionUri;
import manage.modules.action.service.ActionRangeService;
import manage.modules.action.service.ActionUriService;
import manage.modules.menu.model.Menu;
import manage.modules.menu.model.MenuFolders;
import manage.modules.menu.service.MenuService;
import manage.modules.rbac.model.ActionOfMenu;
import manage.modules.rbac.model.ActionOfRange;
import manage.modules.rbac.model.ActionOfRole;
import manage.modules.rbac.model.MenuOfRole;
import manage.modules.rbac.service.RBACService;
import manage.modules.role.model.Role;
import manage.modules.role.service.RoleService;
import manage.modules.worker.service.WorkerService;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.simple.ParameterizedSingleColumnRowMapper;

public class RBACServiceImpl implements RBACService {

	private WorkerService workerService;
	private MenuService menuService;
	private RoleService roleService;
	private ActionUriService actionUriService;
	private ActionRangeService actionRangeService;
	private BaseService baseService;

	@Override
	public List<Map<String, Object>> listWorkerbyRoleId(Integer roleId) {
		return roleService.listWorkerbyRoleId(roleId);
	}

	@Override
	public List<MenuFolders> listMenuByRoleId(Integer roleId) {
		return menuService.listMenuByRoleId(roleId);
	}

	@Override
	public List<MenuFolders> listMenusByWorkerId(Long workerId) {
		return menuService.listMenusByWorkerId(workerId);
	}

	@Override
	public List<Map<String, Object>> listMenuOfRoleByRoleId(Integer roleId) {
		return roleService.listMenuOfRoleByRoleId(roleId);
	}

	@Override
	public List<Map<String, Object>> listMenuOfRoleByMenuId(Integer menuId) {
		return roleService.listMenuOfRoleByRoleId(menuId);
	}

	@Override
	public List<Map<String, Object>> listRBACByWorkerId(Long workerId) {
		List<Role> workerRoles = workerService.listWorkerOfRole(workerId);
		if (CollectionUtils.isNotEmpty(workerRoles)) {
			List<Map<String, Object>> workerRbacList = new ArrayList<Map<String, Object>>();
			for (Role role : workerRoles) {
				Map<String, Object> roleRbacLayout = listRBACLayoutByRoleId(role
						.getRoleId());
				workerRbacList.add(roleRbacLayout);
			}
			return workerRbacList;
		}
		return null;
	}

	@Override
	public List<Map<String, Object>> listActionOfMenuByMenuId(Integer menuId) {
		String sql = "select aom.*,au.actionName,au.note from "
				+ TableNameUtil.getTableName(ActionOfMenu.class) + " aom join "
				+ TableNameUtil.getTableName(ActionUri.class)
				+ " au on aom.actionId=au.actionId" + " where aom.menuId = ?";
		return baseService.getJdbcOperations().queryForList(sql, menuId);
	}

	@Override
	public List<Map<String, Object>> listActionOfRangeByActionId(
			Integer actionId) {
		String sql = "select aor.*,ar.rangeName from "
				+ TableNameUtil.getTableName(ActionOfRange.class)
				+ " aor join " + TableNameUtil.getTableName(ActionRange.class)
				+ " ar on aor.rangeId=ar.rangeId where aor.actionId = ?";
		return baseService.getJdbcOperations().queryForList(sql, actionId);
	}

	@Override
	public List<ActionOfMenu> listActionOfMenuByRoleIdAndMenuId(Integer roleId,
			Integer menuId) {
		String sql = "select aom.*,au.actionUri from  "
				+ TableNameUtil.getTableName(ActionOfRole.class) + " aor join "
				+ TableNameUtil.getTableName(ActionOfMenu.class)
				+ " aom  on aom.actionId=aor.actionId join "
				+ TableNameUtil.getTableName(ActionUri.class)
				+ " au on au.actionId=aom.actionId"
				+ " where aor.roleId=? and aom.menuId=?";
		return baseService.getJdbcOperations().query(
				sql,
				ParameterizedBeanPropertyRowMapper
						.newInstance(ActionOfMenu.class), roleId, menuId);
	}

	@Override
	public List<ActionOfRange> listActionOfRangeByRoleIdAndActionId(
			Integer roleId, Integer actionId) {
		String sql = "select ar.actionId,ar.isRadio,aor.rangeId from "
				+ TableNameUtil.getTableName(ActionOfRole.class) + " aor join "
				+ TableNameUtil.getTableName(ActionOfRange.class)
				+ " ar on ar.actionId = aor.actionId"
				+ " where aor.roleId=? and ar.actionId=?";
		return baseService.getJdbcOperations().query(
				sql,
				ParameterizedBeanPropertyRowMapper
						.newInstance(ActionOfRange.class), roleId, actionId);
	}

	@Override
	public void menuAddActionUri(ActionOfMenu actionOfMenu) {
		String sql = "insert into "
				+ TableNameUtil.getTableName(ActionOfMenu.class)
				+ "(menuId,actionId)" + "values(:menuId,:actionId)";
		baseService.getNamedJdbc().update(sql,
				new BeanPropertySqlParameterSource(actionOfMenu));
	}

	@Override
	public void deleteActionUriByMenuId(Integer menuId) {
		baseService.delete("menuId", menuId, ActionOfMenu.class);
	}

	@Override
	public Map<String, Object> listRBACLayoutByRoleId(Integer roleId) {
		Map<String, Object> roleRabcLayoutMap = new HashMap<String, Object>();
		List<MenuFolders> menuFolders = listMenuByRoleId(roleId);
		roleRabcLayoutMap.put(Constants.ROLEMENUFOLDERS, menuFolders);
		for (MenuFolders menuFolder : menuFolders) {
			for (Menu childMenu : menuFolder.getChilds()) {
				List<ActionOfMenu> aoms = listActionOfMenuByRoleIdAndMenuId(
						roleId, childMenu.getMenuId());
				roleRabcLayoutMap.put(
						Constants.RBAC_MA_PRIFX + childMenu.getMenuId(), aoms);
				for (ActionOfMenu actionOfMenu : aoms) {
					Integer actionId = actionOfMenu.getActionId();
					List<ActionOfRange> aors = listActionOfRangeByRoleIdAndActionId(
							roleId, actionId);
					roleRabcLayoutMap.put(
							Constants.RBAC_MAR_PRIFX + childMenu.getMenuId()
									+ "_" + actionId, aors);
				}
			}
		}
		return roleRabcLayoutMap;
	}

	@Override
	public Map<String, Object> listRBACLayout() {
		Map<String, Object> rabcLayoutMap = new HashMap<String, Object>();
		List<MenuFolders> menuFolders = menuService.listMenuFolders();
		rabcLayoutMap.put(Constants.MENUFOLDERS, menuFolders);
		for (MenuFolders menuFolder : menuFolders) {
			for (Menu childMenu : menuFolder.getChilds()) {
				List<Map<String, Object>> actions = listActionOfMenuByMenuId(childMenu
						.getMenuId());
				rabcLayoutMap.put(
						Constants.RBAC_MA_PRIFX + childMenu.getMenuId(),
						actions);
				for (Map<String, Object> action : actions) {
					Integer actionId = MapUtils.getInteger(action, "actionId",
							-1);
					if (-1 != actionId) {
						List<Map<String, Object>> ranges = listActionOfRangeByActionId(actionId);
						rabcLayoutMap.put(
								Constants.RBAC_MAR_PRIFX
										+ childMenu.getMenuId() + "_"
										+ actionId, ranges);
					}
				}
			}
		}
		return rabcLayoutMap;
	}

	@Override
	public void actionAddActionRange(ActionOfRange actionOfRange) {
		baseService.insert(actionOfRange);
	}

	@Override
	public void deleteActionRangeByActionId(Integer actionId) {
		baseService.delete("actionId", actionId, ActionOfRange.class);
	}

	@Override
	public List<ActionUri> listAllActionUri() {
		return actionUriService.listAll();
	}

	@Override
	public List<ActionRange> listAllActionRange() {
		return actionRangeService.listAll();
	}

	@Override
	public boolean insertMenuOfRole(Integer menuId, Integer roleId) {
		MenuOfRole mor = new MenuOfRole();
		mor.setMenuId(menuId);
		mor.setRoleId(roleId);
		int count = baseService.insert(mor);
		return NumberAndBoolean.numberToBoolean(count);
	}

	@Override
	public boolean deleteMenuOfRole(Integer menuId, Integer roleId) {
		Map<String, Object> fields = new HashMap<String, Object>();
		fields.put("menuId", menuId);
		fields.put("roleId", roleId);
		int count = baseService.deleteByFields(fields, MenuOfRole.class);
		return NumberAndBoolean.numberToBoolean(count);
	}

	@Override
	public List<ActionOfRole> listActionOfRoleByRoleId(Integer roleId) {
		return baseService.listByField("roleId", roleId, ActionOfRole.class);
	}

	@Override
	public boolean deleteActionOfRole(Integer actionId, Integer roleId) {
		Map<String, Object> fields = new HashMap<String, Object>();
		fields.put("actionId", actionId);
		fields.put("roleId", roleId);
		int count = baseService.deleteByFields(fields, ActionOfRole.class);
		return NumberAndBoolean.numberToBoolean(count);
	}

	@Override
	public boolean insertActionOfRole(Integer actionId, Integer roleId,
			Integer rangeId) {
		ActionOfRole aor = new ActionOfRole();
		aor.setActionId(actionId);
		aor.setRoleId(roleId);
		aor.setRangeId(rangeId);
		int count = baseService.insert(aor);
		return NumberAndBoolean.numberToBoolean(count);
	}

	@Override
	public List<Integer> listRangIdByActionAndRole(Integer actionId,
			Integer roleId) {
		String sql = "select rangeId from "
				+ TableNameUtil.getTableName(ActionOfRole.class)
				+ " where roleId=? and actionId=?";
		return baseService.getJdbcOperations().query(sql,
				ParameterizedSingleColumnRowMapper.newInstance(Integer.class),
				roleId, actionId);
	}

	@Override
	public boolean deleteActionOfRole(ActionOfRole actionOfRole) {
		String sql = "delete from "
				+ TableNameUtil.getTableName(ActionOfRole.class)
				+ " where roleId=? and actionId=? and rangeId=? limit 1";
		int count = baseService.getJdbcOperations().update(sql,
				actionOfRole.getRoleId(), actionOfRole.getActionId(),
				actionOfRole.getRangeId());
		return NumberAndBoolean.numberToBoolean(count);
	}

	@Override
	public boolean hasPermissions(String uri, List<Map<String, Object>> rbac) {
		return hasPermissions(uri, rbac, null);
	}

	@SuppressWarnings("unchecked")
	public boolean hasPermissions(String uri, List<Map<String, Object>> rbac,
			String rangeValues) {
		uri = StringUtils.trim(uri);
		if (StringUtils.isNotBlank(uri) && CollectionUtils.isNotEmpty(rbac)) {
			for (Map<String, Object> roleRbac : rbac) {
				List<MenuFolders> menuFolders = (List<MenuFolders>) roleRbac
						.get(Constants.ROLEMENUFOLDERS);
				if (CollectionUtils.isNotEmpty(menuFolders)) {
					for (MenuFolders menuFolder : menuFolders) {
						// 父菜单验证
						if (StringUtils.startsWith(menuFolder.getMenu()
								.getUrl(), uri)) {
							return true;
						}
						for (Menu childMenu : menuFolder.getChilds()) {
							// 子菜单验证
							if (StringUtils.startsWith(childMenu.getUrl(), uri)) {
								return true;
							}
							List<ActionOfMenu> actionOfMenus = (List<ActionOfMenu>) roleRbac
									.get(Constants.RBAC_MA_PRIFX
											+ childMenu.getMenuId());
							if (CollectionUtils.isNotEmpty(actionOfMenus)) {
								for (ActionOfMenu actionOfMenu : actionOfMenus) {
									// ActionUri验证
									if (StringUtils.equals(
											actionOfMenu.getActionUri(), uri)) {
										return true;
									}
									// rangeValues不等于空时就需要验证数据范围
									if (StringUtils.isNotEmpty(rangeValues)) {
										List<ActionOfRange> aors = (List<ActionOfRange>) roleRbac
												.get(Constants.RBAC_MAR_PRIFX
														+ childMenu.getMenuId()
														+ "_"
														+ actionOfMenu
																.getActionId());
										if (CollectionUtils.isNotEmpty(aors)) {
											for (ActionOfRange actionOfRange : aors) {
												String[] rangeIds = rangeValues
														.split(",");
												// 验证数据范围，有一个存在就返回true
												for (String rangeId : rangeIds) {
													if (!StringUtils
															.equals(String
																	.valueOf(actionOfRange
																			.getRangeId()),
																	"0")
															&& StringUtils
																	.equals(String
																			.valueOf(actionOfRange
																					.getRangeId()),
																			rangeId)) {
														return true;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return false;
	}

	@Override
	public boolean hasPermissions(String uri, Long workerId) {
		return hasPermissions(uri, listRBACByWorkerId(workerId));
	}

	@Override
	public Integer getRangeValue(String uri, Long workerId) {
		ActionUri actionUri = actionUriService.select(uri);
		int temp = 1;
		if (null != actionUri) {
			List<Role> roles = workerService.listWorkerOfRole(workerId);
			if (CollectionUtils.isNotEmpty(roles)) {
				for (Role role : roles) {
					Map<String, Object> conditions = new HashMap<String, Object>();
					conditions.put("roleId", role.getRoleId());
					conditions.put("actionId", actionUri.getActionId());
					List<ActionOfRole> aors = baseService.listByFields(
							conditions, ActionOfRole.class);
					if (CollectionUtils.isNotEmpty(aors)) {
						int orderNo = -1000;
						for (ActionOfRole actionOfRole : aors) {
							Integer rangeId = actionOfRole.getRangeId();
							ActionRange ar = actionRangeService.select(rangeId);
							if (ar != null && ar.getOrderNo() != null
									&& ar.getOrderNo() > orderNo) {
								orderNo = ar.getOrderNo();
								temp = rangeId;
							}
						}
					}
				}
			}
		}
		return temp;
	}

	public void setMenuService(MenuService menuService) {
		this.menuService = menuService;
	}

	public void setRoleService(RoleService roleService) {
		this.roleService = roleService;
	}

	public void setActionUriService(ActionUriService actionUriService) {
		this.actionUriService = actionUriService;
	}

	public void setActionRangeService(ActionRangeService actionRangeService) {
		this.actionRangeService = actionRangeService;
	}

	public void setBaseService(BaseService baseService) {
		this.baseService = baseService;
	}

	public void setWorkerService(WorkerService workerService) {
		this.workerService = workerService;
	}

}
